#include <stdexcept>
#include "prot-filer/compressed_filer.h"

namespace prot_filer
{
bool CompressedReader::_open(const std::string& name)
{
    file.open(name.c_str(), std::ios::binary | std::ios::in);
    angles_mapping = helper.read_mapping(file);
    const unsigned int nres = angles_mapping->get_nres();
    angles_data = new AnglesData(nres, angles_mapping);
    _atoms_in_a_protein = nres * ATOMS_PER_RESIDUE;
    end_position = helper.get_last_position(file, angles_mapping, angles_data);
    return file;
}

bool CompressedReader::read(BasicProtein& protein, size_t n)
{
    checkOpen();
    bool end = load(n);
    backbones_utils::angles2coord(protein, *angles_mapping, *angles_data);
    return end;
}

bool CompressedReader::read(Protein& protein, size_t n)
{
    checkOpen();
    BasicProtein basic_protein(_atoms_in_a_protein);
    bool end = read(basic_protein, n);
    for (int atom_number(0); atom_number < _atoms_in_a_protein; ++atom_number)
    {
        const ATOM atom = basic_protein[atom_number];
        protein.addItem(Coord3d(atom.x / 10.0, atom.y / 10.0, atom.z / 10.0));
    }
    return end;
}

bool CompressedReader::read(AnglesData& protein, size_t n)
{
    checkOpen();
    if (_atoms_in_a_protein != int(ATOMS_PER_RESIDUE * protein.nres))
        throw std::runtime_error("read(AnglesData) received a protein with the wrong number of residues");
    bool end = load(n);
    protein = *angles_data;
    return end;
}

bool CompressedReader::load(size_t position)
{
    if (position >= end_position)
    {
        return true;
    }
    else
    {
        if (position != current_position)
        {
            int64_t stream_position = helper.get_mapping_file_size_in_bytes(angles_mapping);
            stream_position += size_t(helper.get_structure_file_size_in_bytes(angles_data)) * size_t(position);
            file.seekg(stream_position);
        }

        CustomReadBuffer buffer(angles_data->get_angles_mapping().get_mapping_size(), file);
        for (unsigned int id = 0 ; id < angles_data->nres - 1 ; ++id)
        {
            buffer.read(angles_data->angles[id]);
        }
        unsigned char temp;
        if (!(file >> temp) || temp != END_OF_DATA)
            std::cerr << "Read error: Read ended without reaching end of structure." << std::endl;

        current_position = position + 1;
        return false;
    }
}

bool CompressedWriter::_open(const std::string& name)
{
    file.open(name.c_str(), std::ios::binary | std::ios::out);
    return file;
}

void CompressedWriter::write(const AnglesData& angles_data)
{
    checkOpen();
    if (first_time)
    {
        helper.write_mapping(file, angles_data.get_angles_mapping());
    }

    CustomWriteBuffer buffer(angles_data.get_angles_mapping().get_mapping_size(), file);
    for (unsigned int id = 0; id < angles_data.nres - 1 ; ++id)
    {
        buffer.write(angles_data.angles[id]);
    }
    buffer.flush_buffer();
    file << END_OF_DATA;
}

void CompressedWriter::_close()
{
    file.close();
}
}
